/*
 * Copyright 2009 Codecarpet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.codecarpet.fbconnect;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import temporary.CcUtil;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.view.animation.Animation.AnimationListener;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class FBDialog extends FrameLayout {

	private static final String LOG = FBDialog.class.getSimpleName();

	// /////////////////////////////////////////////////////////////////////////////////////////////////
	// global

	private static final String DEFAULT_TITLE = "Connect to Facebook";
	private static final String STRING_BOUNDARY = "3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f";

	private static final int FACEBOOK_BLUE = CcUtil.rgbFloatToInt(0.42578125f,
			0.515625f, 0.703125f, 1.0f);
	private static final int BORDER_GRAY = CcUtil.rgbFloatToInt(0.3f, 0.3f,
			0.3f, 0.8f);

	private static final int kTransitionDuration = 200; // changed from original
	// 300ms

	private static final int kTitleMarginX = 8;
	private static final int kTitleMarginY = 4;
	private static final int kPadding = 10;
	private static final int kBorderWidth = 10;

	// /////////////////////////////////////////////////////////////////////////////////////////////////

	private IDialogDelegate mDelegate;
	protected FBSession mSession;
	private URL mLoadingURL;
	protected WebView mWebView;
	private TextView mTitleLabel;
	private ImageButton mCloseButton;
	@SuppressWarnings("unused")
	private int mOrientation; // see ActivityInfo.SCREEN_ORIENTATION*
	@SuppressWarnings("unused")
	private boolean mShowingKeyboard;
	private CookieManager cookieManager;

	private LinearLayout mContent;
	protected Activity mContext;
	private CookieSyncManager cookieSyncMngr;

	// public FBDialog(Context context) {
	// this(context, FBSession.getSession());
	// }

	public FBDialog(Activity context, FBSession session) {
		super(context);
		cookieSyncMngr = CookieSyncManager.createInstance(context);
		mContext = context;
		mDelegate = null;
		
		mSession = session;// null;

		mLoadingURL = null;
		mOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; // UIDeviceOrientationUnknown;
		mShowingKeyboard = false;

		// http://groups.google.com/group/android-developers/browse_thread/thread/a0b71c59fb33b94a/5d996451f43f507b?lnk=gst&q=ondraw#5d996451f43f507b
		setWillNotDraw(false);

		int contentPadding = kPadding + kBorderWidth;
		setPadding(contentPadding, contentPadding, contentPadding,
				contentPadding);

		// main content of popup window
		mContent = new LinearLayout(context);
		mContent.setOrientation(LinearLayout.VERTICAL);
		mContent.setBackgroundColor(Color.WHITE);
		mContent.setLayoutParams(new LayoutParams(
				ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.FILL_PARENT));

		RelativeLayout title = new RelativeLayout(context);
		title.setLayoutParams(new LayoutParams(
				ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT));

		mTitleLabel = new TextView(context);
		mTitleLabel.setText(DEFAULT_TITLE);
		mTitleLabel.setBackgroundColor(FACEBOOK_BLUE);
		mTitleLabel.setTextColor(Color.WHITE);
		mTitleLabel.setTypeface(Typeface.DEFAULT_BOLD);
		mTitleLabel.setPadding(kTitleMarginX, kTitleMarginY, kTitleMarginX,
				kTitleMarginY);
		mTitleLabel.setLayoutParams(new LayoutParams(
				ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.WRAP_CONTENT));

		// FB icon on the left side of the dialog title
		Drawable iconDrawable = CcUtil.getDrawable(getClass(),
				"com/codecarpet/fbconnect/resources/fbicon.png");

		// close icon on the right side of the dialog title
		Drawable closeDrawable = CcUtil.getDrawable(getClass(),
				"com/codecarpet/fbconnect/resources/close.png");

		// FB icon is part of TextWiev
		mTitleLabel.setCompoundDrawablePadding(5); 
		// padding
		mTitleLabel.setCompoundDrawablesWithIntrinsicBounds(iconDrawable, null,
				null, null);
		title.addView(mTitleLabel);

		// close icon is on standalone button, next to title TextView
		mCloseButton = new ImageButton(context);
		mCloseButton.setBackgroundColor(Color.TRANSPARENT);
		mCloseButton.setImageDrawable(closeDrawable);
		mCloseButton.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View view, MotionEvent event) {
				int action = event.getAction();
				switch (action) {
				case MotionEvent.ACTION_DOWN:
					mTitleLabel.setBackgroundColor(BORDER_GRAY);
					return true;
				case MotionEvent.ACTION_UP:
					mTitleLabel.setBackgroundColor(FACEBOOK_BLUE);
					dismiss(true);
					return true;
				}
				return false;
			}
		});
		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
				RelativeLayout.LayoutParams.WRAP_CONTENT,
				RelativeLayout.LayoutParams.WRAP_CONTENT);
		lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
		title.addView(mCloseButton, lp);

		mContent.addView(title);
		mWebView = new WebView(context);
		mWebView.setLayoutParams(new LayoutParams(
				ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.FILL_PARENT));
		mWebView.setWebViewClient(new WebViewClientImpl());

		WebSettings webSettings = mWebView.getSettings();
		webSettings.setJavaScriptEnabled(true);
		webSettings.setDefaultTextEncodingName("UTF-8");

		mContent.addView(mWebView);

		addView(mContent);
	}

	// /////////////////////////////////////////////////////////////////////////////////////////////////

	public FBSession getSession() {
		return mSession;
	}

	public void setSession(FBSession session) {
		mSession = session;
	}

	public IDialogDelegate getDelegate() {
		return mDelegate;
	}

	public void setDelegate(IDialogDelegate delegate) {
		mDelegate = delegate;
	}

	// /////////////////////////////////////////////////////////////////////////////////////////////////
	// private

	private void drawRect(Canvas context, Rect rect, int fillColor, float radius) {
		Paint paint = new Paint();
		paint.setStyle(Paint.Style.FILL);
		paint.setColor(fillColor);
		paint.setAntiAlias(true);
		if (radius > 0) {
			context.drawRoundRect(new RectF(rect), radius, radius, paint);
		} else {
			context.drawRect(rect, paint);
		}
	}

	@SuppressWarnings("unused")
	private void strokeLines(Canvas context, RectF rect, int strokeColor) {
		Paint paint = new Paint();
		paint.setStyle(Paint.Style.STROKE);
		paint.setColor(strokeColor);
		paint.setStrokeWidth(1.0f);

		context.drawRect(0.5f, 0.5f, 0.5f, 0.5f, paint);
	}

	@SuppressWarnings("unused")
	private void bounce1AnimationStopped() {
		ScaleAnimation scale = new ScaleAnimation(1.1f, 0.9f, 1.1f, 0.9f,
				ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
				ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
		scale.setDuration(kTransitionDuration / 2);
		scale.setAnimationListener(new AnimationListener() {
			public void onAnimationEnd(Animation arg0) {
				bounce2AnimationStopped();
			}

			public void onAnimationRepeat(Animation arg0) {
			}

			public void onAnimationStart(Animation arg0) {
			}
		});
		startAnimation(scale);
	}

	private void bounce2AnimationStopped() {
		ScaleAnimation scale = new ScaleAnimation(0.9f, 1, 0.9f, 1,
				ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
				ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
		scale.setDuration(kTransitionDuration / 2);
		startAnimation(scale);
	}

	private URL generateURL(String baseURL, Map<String, String> params)
			throws MalformedURLException {

		StringBuilder sb = new StringBuilder(baseURL);
		Iterator<Entry<String, String>> it = params.entrySet().iterator();
		if (it.hasNext()) {
			sb.append('?');
		}
		while (it.hasNext()) {
			Entry<String, String> entry = it.next();
			sb.append(entry.getKey());
			sb.append('=');
			sb.append(CcUtil.encode(entry.getValue()));
			if (it.hasNext()) {
				sb.append('&');
			}
		}
		return new URL(sb.toString());
	}

	private String generatePostBody(Map<String, String> params) {
		StringBuilder body = new StringBuilder();
		StringBuilder endLine = new StringBuilder("\r\n--").append(
				STRING_BOUNDARY).append("\r\n");

		body.append("--").append(STRING_BOUNDARY).append("\r\n");

		for (Entry<String, String> entry : params.entrySet()) {
			body.append("Content-Disposition: form-data; name=\"").append(
					entry.getKey()).append("\"\r\n\r\n");
			String value = entry.getValue();
			if ("user_message_prompt".equals(entry.getKey())) {
				body.append(value);
			} else {
				body.append(CcUtil.encode(value));
			}

			body.append(endLine);
		}

		return body.toString();
	}

	private void postDismissCleanup() {
		mContext.finish();
	}

	private void dismiss(boolean animated) {
		dialogWillDisappear();
		mLoadingURL = null;
		if (animated) {

			AlphaAnimation animation = new AlphaAnimation(1, 0);
			animation.setDuration(kTransitionDuration);

			postDismissCleanup();
			startAnimation(animation);
		} else {
			postDismissCleanup();
		}
	}

	@SuppressWarnings("unused")
	private void cancel() {
		dismissWithSuccess(false, true);
	}

	// /////////////////////////////////////////////////////////////////////////////////////////////////
	// Object

	// /////////////////////////////////////////////////////////////////////////////////////////////////
	// View

	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		Rect canvasClipBounds = new Rect(canvas.getClipBounds());

		Rect grayRect = new Rect(canvasClipBounds);
		grayRect.inset(kPadding, kPadding);
		drawRect(canvas, grayRect, BORDER_GRAY, 10f);
	}

	// /////////////////////////////////////////////////////////////////////////////////////////////////
	// WebViewClient

	public final class WebViewClientImpl extends WebViewClient {

		// private Dialog dialog = null;

		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			URI uri;
			try {
				uri = new URI(url);

				Log.d("FBDialog", "Web view URL = " + url);

				// see issue #2
				if (!uri.isAbsolute()) {
					Log
							.e(LOG,
									"Something went wrong. You probably forgot to specify API key and secret?");
					// I don't return false from here, because I prefer nasty
					// NPE over 'Page not available' message in browser
				}

				// fbconnect is not always the scheme but sometimes after
				// hostname
				if (uri.getScheme() != null && url.contains("fbconnect:")) {
					if (url.contains("fbconnect://cancel")
							|| url.contains("fbconnect:cancel")) {
						dismissWithSuccess(false, true);
					} else {
						dialogDidSucceed(uri);
					}
					return true;
				} else if (mLoadingURL.toExternalForm().equals(url)) {
					return false;
				} else {// if (navigationType ==
					// UIWebViewNavigationTypeLinkClicked) {
					if (mDelegate != null
							&& !mDelegate.dialogShouldOpenUrlInExternalBrowser(
									FBDialog.this, uri.toURL())) {
						return true;
					}

					// [[UIApplication sharedApplication] openURL:request.URL];
					// return false;
					// } else {
					return false;
					// }
				}
			} catch (URISyntaxException e) {
				e.printStackTrace();
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
			return false;
		}

		public void onPageStarted(WebView view, String url, Bitmap favicon) {
			super.onPageStarted(view, url, favicon);

			FBProgressDialog.show(FBDialog.this.getContext(), null);
		}

		@Override
		public void onPageFinished(WebView view, String url) {
			super.onPageFinished(view, url);
			@SuppressWarnings("unused")
			CookieManager mgr = CookieManager.getInstance();
			FBProgressDialog.hide(FBDialog.this.getContext());
			cookieSyncMngr.sync();
		}

	}

	public String getTitle() {
		return mTitleLabel.getText().toString();
	}

	public void setTitle(String title) {
		mTitleLabel.setText(title);
	}

	public void show() {
		load();
	}

	protected void dismissWithSuccess(boolean success, boolean animated) {
		if (mDelegate != null) {
			if (success) {
				mDelegate.dialogDidSucceed(this);
			} else {
				mDelegate.dialogDidCancel(this);
			}
		}
		dismiss(animated);
	}

	protected void dismissWithError(Throwable error, boolean animated) {
		mDelegate.dialogDidFailWithError(this, error);
		dismiss(animated);
	}

	protected void load() {
		// Intended for subclasses to override
	}

	protected void loadURL(String url, String method,
			Map<String, String> getParams, Map<String, String> postParams)
			throws MalformedURLException {
		// by default we send the cookies
		loadURL(url, method, getParams, postParams, true);
	}

	protected void loadURL(String url, String method,
			Map<String, String> getParams, Map<String, String> postParams,
			boolean sendCookies) throws MalformedURLException {

		cookieManager = CookieManager.getInstance();
		cookieManager.setAcceptCookie(true);

		mLoadingURL = generateURL(url, getParams);

		HttpURLConnection conn = null;
		OutputStream out = null;
		InputStream in = null;
		System.out.println(mLoadingURL.toString());
		try {
			if ("GET".equals(method)) {
				mWebView.loadUrl(mLoadingURL.toString());

			} else {
				String response = postURL(url, postParams, sendCookies);
				if (response != "") {
					// In case there are several Set-cookie fields in the
					// header
					for (int i = 0; true; i++) {
						String hdrKey = conn.getHeaderFieldKey(i);
						String hdrVal = conn.getHeaderField(i);
						if (hdrKey == null) {
							if (hdrVal == null)
								break; // end of the header
							continue; // in some implementations, first
							// header
							// key is empty
						}
						Log.i(LOG, "url header: " + hdrKey + "=" + hdrVal);
						if (hdrKey.equalsIgnoreCase("set-cookie")) {
							cookieManager.setCookie(url, hdrVal);
						}
					}
					cookieSyncMngr.sync();

					URI uri = new URI(url);
					// we need to load the data with base URL or else the
					// webview doesn't know how to build relative URLs
					mWebView.loadDataWithBaseURL("http://" + uri.getHost(),
							response, "text/html", "UTF-8", "http://"
									+ uri.getHost());

					mWebView.loadData(response, "text/html", "UTF-8");
				} else if (sendCookies) {
					// If the page has not loaded the first time we try to
					// reload it without sending the cookies
					// the issue is we can't delete the cookies on logout...
					loadURL(url, method, getParams, postParams, false);
				}
			}

		} catch (URISyntaxException e) {
			Log.e(LOG, "Error on url format", e);
		} catch (IOException e) {
			Log.e(LOG, "Error while opening page", e);
			// If the page has not loaded the first time we try to reload it
			// without sending the cookies
			// the issue is we can't delete the cookies on logout...
			if (sendCookies) {
				loadURL(url, method, getParams, postParams, false);
			}
		} finally {
			CcUtil.close(in);
			CcUtil.close(out);
			CcUtil.disconnect(conn);
		}

	}

	protected void dialogWillAppear() {
	}

	protected void dialogWillDisappear() {
	}

	protected void dialogDidSucceed(URI uri) {
		dismissWithSuccess(true, true);
	}

	public static abstract class FBDialogDelegate implements IDialogDelegate {

		/**
		 * Called when the dialog succeeds and is about to be dismissed.
		 */
		public void dialogDidSucceed(FBDialog dialog) {
		}

		/**
		 * Called when the dialog is cancelled and is about to be dismissed.
		 */
		public void dialogDidCancel(FBDialog dialog) {
		}

		/**
		 * Called when dialog failed to load due to an error.
		 */
		public void dialogDidFailWithError(FBDialog dialog, Throwable error) {
		}

		/**
		 * Asks if a link touched by a user should be opened in an external
		 * browser.
		 * 
		 * If a user touches a link, the default behavior is to open the link in
		 * the Safari browser, which will cause your app to quit. You may want
		 * to prevent this from happening, open the link in your own internal
		 * browser, or perhaps warn the user that they are about to leave your
		 * app. If so, implement this method on your delegate and return NO. If
		 * you warn the user, you should hold onto the URL and once you have
		 * received their acknowledgement open the URL yourself using
		 * [[UIApplication sharedApplication] openURL:].
		 */
		public boolean dialogShouldOpenUrlInExternalBrowser(FBDialog dialog,
				URL url) {
			return false;
		}

	}

	public String postURL(String url, Map<String, String> postParams,
			boolean sendCookies) throws IOException {

//		OutputStream out = null;
//		InputStream in = null;
		// HttpURLConnection conn = (HttpURLConnection) new
		// URL(url).openConnection();
		// conn.setDoOutput(true);
		// conn.setDoInput(true);

		HttpClient client = new DefaultHttpClient();
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		Iterator<Entry<String, String>> keySetIterator = postParams.entrySet()
				.iterator();
		while (keySetIterator.hasNext()) {
			Entry<String, String> postData = (Entry<String, String>) keySetIterator
					.next();
			formparams.add(new BasicNameValuePair(postData.getKey(), postData
					.getValue()));
		}
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
				"UTF-8");
		HttpPost httppost = new HttpPost(url);
		httppost.setEntity(entity);
		ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
			public byte[] handleResponse(HttpResponse response)
					throws ClientProtocolException, IOException {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					return EntityUtils.toByteArray(entity);
				} else {
					return null;
				}
			}
		};
		byte[] response = client.execute(httppost, handler);
		return new String(response);
		// conn.setRequestMethod("POST");
		// String contentType = "multipart/form-data; boundary=" +
		// STRING_BOUNDARY;
		// conn.setRequestProperty("Content-Type", contentType);
		//
		// // Cookies are used in FBPermissionDialog and FBFeedDialog
		// // to
		// // retrieve logged user
		// if (sendCookies)
		// conn.setRequestProperty("Cookie", cookieManager.getCookie(url));
		//
		// conn.connect();
		//
		// out = conn.getOutputStream();
		// String body = generatePostBody(postParams);
		// if (body != null) {
		// out.write(body.getBytes("UTF-8"));
		// }
		// in = conn.getInputStream();

		//return CcUtil.getResponse(in).toString();
	}
}
